import datetime
import pymongo
import copy
import Core.Gadget as Gadget
import Core.Algorithm as Algo
import Core.IO as IO
from Factors.General import *

def RatioCAGRGrothCallback(dfFundamental, factorName, params):
    #
    ratioName = params["RatioName"]
    numerator = params["Numerator"]
    denominator = params["Denominator"]
    #
    dfFundamental[ratioName] = dfFundamental[numerator + "_LYR"] / dfFundamental[denominator + "_LYR"]
    #
    # fields = [ratioName]
    params["Fields"] = [ratioName]
    CAGRGrowthCallback(dfFundamental, factorName, params)


def RatioYoYGrowthCallback(dfFundamental, factorName, params):
    #
    ratioName = params["RatioName"]
    numerator = params["Numerator"]
    denominator = params["Denominator"]
    #
    dfFundamental[ratioName] = dfFundamental[numerator] / dfFundamental[denominator]
    #
    # fields = [ratioName]
    params["Fields"] = [ratioName]
    CAGRGrowthCallback(dfFundamental, factorName, params)


# ---计算Growth Factor---
# Method#1 : CalcGeneralGrowthFactor 不需要Callback---
# Method#2 : CalcFundamentalFactor + Callback--- 未能实现
def CalcGrowthFactor(database, instruments, datetime1, datetime2, update=False):

    #
    growthFields = ["TotalRevenue", "OperatingProfit", "OperatingProfit1", "NetIncome2",
                    "OperatingCashFlow", "FreeCashFlow", "TotalEquity", "TotalAsset", "EPS"]
    for growthField in growthFields:
        #
        for year in [1, 3, 5]:
            # for period in range(1, 4+1):
            period = 4
            params = {"Period": period, "Year": year, "GrowthType": "CAGR"}
            CalcFundamentalFactor(database, instruments, datetime1, datetime2,
                                  factorName="Growth_CAGR_" + growthField + "_" + str(year) + "Yr",
                                  callback=CAGRGrowthCallback,
                                  fields=[growthField], params=params, outputField=["Remark"],
                                  update=update, test=False)

    growthFields = ["TotalRevenue", "OperatingProfit", "OperatingProfit1", "NetIncome2", "EPS"]
    params = {"GrowthType": "YoY"}
    for growthField in growthFields:
        CalcFundamentalFactor(database, instruments, datetime1, datetime2,
                              factorName="Growth_YoY_" + growthField,
                              callback=CAGRGrowthCallback,
                              fields=[growthField], params=params, outputField=["Remark"],
                              update=update, test=False)

    # ---Ratio Growth (Need to Calculate)---
    ratioGrowthParams = []
    ratioGrowthParams.append({"RatioName": "ROE", "Numerator": "NetIncome2", "Denominator": "TotalEquity"})
    ratioGrowthParams.append({"RatioName": "ProfitMargin_OperatingProfit",  "Numerator": "OperatingProfit", "Denominator": "Sales"})
    ratioGrowthParams.append({"RatioName": "ProfitMargin_OperatingProfit1", "Numerator": "OperatingProfit1", "Denominator": "Sales"})

    #
    for ratioParam in ratioGrowthParams:
        for year in [1, 3, 5]:
            param = copy.deepcopy(ratioParam)
            param["GrowthType"] = "CAGR"
            param["Period"] = 4
            param["Year"] = year
            CalcFundamentalFactor(database, instruments, datetime1, datetime2,
                                  factorName="Growth_CAGR_" + ratioParam["RatioName"] + "_" + str(year) + "Yr",
                                  callback=RatioCAGRGrothCallback,
                                  fields=[ratioParam["Numerator"], ratioParam["Denominator"]],
                                  params=param, outputField=["Remark"],
                                  update=update, test=False)

    #
    for ratioParam in ratioGrowthParams:
        param = copy.deepcopy(ratioParam)
        param["GrowthType"] = "YoY"
        CalcFundamentalFactor(database, instruments, datetime1, datetime2,
                              factorName="Growth_YoY_" + ratioParam["RatioName"],
                              callback=RatioYoYGrowthCallback,
                              fields=[ratioParam["Numerator"], ratioParam["Denominator"]],
                              params=param, outputField=["Remark"],
                              update=update, test=False)


# field, period, year
# 兼容YoY
def CAGRGrowthCallback(dfFundamental, factorName, params={}):
    #
    # print(dfFundamental.head())
    # --- 准备一个临时DF，以ReportDate为Index---
    dfTemp = dfFundamental.set_index(['ReportDate'])
    # print(dfTemp)

    # ---Params Prepared---
    fields = params["Fields"]
    targetField = fields[0]
    growthType = params["GrowthType"]

    if growthType == "CAGR":
        year = params["Year"] # LagYear
        targetPeriod = params["Period"] # BasePeriod
    elif growthType == "YoY":
        year = 1
    elif growthType == "Index":
        year = 1
        targetPeriod = params["Period"]
    else:
        print("Unknown Growth Type")
        return

    #
    dfFundamental[factorName] = np.nan
    dfFundamental["Remark"] = ""

    # ---Loop DataFrame--
    for index, row in dfFundamental.iterrows():
        #
        value = row[targetField]
        #
        period = row["Period"]
        if growthType == "CAGR" and period != targetPeriod:
            continue

        # ---YoY不必指定Period, 以最新Period计算同比 - --
        if growthType == "YoY":
            pass

        if growthType == "Index" and period != targetPeriod:
            continue

        #
        reportDate = row["ReportDate"]
        currentYear = reportDate.year
        # currentYear = pd.to_datetime(reportDate).year
        # print (currentYear)

        #if reportDate == datetime.date(2009,12,31):
        #    kkwood = 0

        # ---Generate LYR Report Date---
        reportDatePrevYear = Gadget.GenerateReportDate(currentYear - year, period, isUTC=False)
        reportDatePrevYear = reportDatePrevYear.date()
        # reportDatePrevYear = pd.to_datetime(reportDatePrevYear)

        # ---Find Previous Statement---
        if reportDatePrevYear in dfTemp.index:
            remark = None
            previousValue = dfTemp.loc[reportDatePrevYear, targetField]
            if previousValue == 0:
                grwothRate = 0
            elif growthType == "Index":
                grwothRate = value / previousValue
            else:
                grwothRate, remark = Algo.CalcGrowthRate(value, previousValue, period=1)
            #
            dfFundamental.at[index, factorName] = grwothRate
            dfFundamental.at[index, "Remark"] = remark
            # dfFundamental.at[index, "Period"] = period
            # dfFundamental.at[index, "Year"] = year

    # print(dfFundamental.head(10))
    pass


# ---Obselote, 并入Cagr Growth---
# ---YoY不必指定Period,以最新Period计算同比---
def GrowthYoYCallback(dfFundamental, factorName, params={}):

    dfFundamental["BasedPeriod"] = np.nan
    fields = params["Fields"]
    targetField = fields[0]
    for index, row in dfFundamental.iterrows():
        #
        value = row[targetField]
        #
        period = row["Period"]
        reportDate = row["ReportDate"]
        currentYear = reportDate.year

        # ---Find Previous Statement---
        reportDatePrevYear = Gadget.GenerateReportDate(currentYear - 1, period, isUTC=False)
        reportDatePrevYear = pd.to_datetime(reportDatePrevYear)

        if reportDatePrevYear in dfFundamental.index:
            previousValue = dfFundamental.loc[reportDatePrevYear, targetField]
            grwothRate, remark = Algo.CalcGrowthRate(value, previousValue, period=1)
            #
            dfFundamental.at[index, factorName] = grwothRate
            dfFundamental.at[index, "BasedPeriod"] = period
            # dfFundamental.at[index, "Year"] = year

    # print(dfFundamental.head())
    pass


# Method#1 : CalcGeneralGrowthFactor 不需要Callback---
def CalcGeneralGrowthFactor(database, instruments, datetime1, datetime2, factorName, fieldName, update=False):
    #
    count = 0
    start = False
    #startSymbol = "000620.SZ"
    for instrument in instruments:
        count = count + 1
        symbol = instrument["Symbol"]

        if count % 100 == 0:
            print("Processing " + symbol + " count " + str(count))
        #print("Calc Growth Factor of " +  symbol + "," + str(count))

        # ---创建容器---
        growthDataSeries = DataSeries.DataSeries(symbol + "_" + factorName + "_Factor")

        # ---确定起始日期---
        realDateTime1, realDateTime2 = DeterminFactorCalculatingRange(database, instrument, datetime1, datetime2,
                                                                      factorName, update)

        if realDateTime1 == realDateTime2:
            print("Unnecessary Update / Delisted or Updated " + symbol)
            continue

        # ---依赖至少6年数据---
        referenceDateTime1 = realDateTime1 + datetime.timedelta(days=-7*365)
        filter = {}
        filter["StdDateTime"] = {}
        filter["StdDateTime"]["$gte"] = referenceDateTime1
        filter["StdDateTime"]["$lte"] = realDateTime2
        sort = [("StdReportDate", pymongo.ASCENDING)]
        statements = database.findWithFilter("Fundamental",symbol + "_Fundamental", filter, sort)

        # ---用于确定，交易日+交易时间，写入数据时间戳---
        bmQuotes = database.getDataSeries("000001.SH_Time_86400_Bar", datetime1=realDateTime1, datetime2=realDateTime2,
                                          instrumentType="Index")

        # ---Loop Statements---
        for statement in statements:
            reportDate = statement["ReportDate"]
            #print("Processing " + reportDate)
            stdDateTime = statement["StdDateTime"]
            stdReportDate = statement["StdReportDate"]
            period = statement["Period"]

            # ---Only Calculate within Datetime1 to Datetime2---
            # ---ReleaseDate Based---
            if stdDateTime < realDateTime1 or stdDateTime > realDateTime2:
                continue

            # ---Complleted Data?---
            if "Values" not in statement:
                print(symbol + "," + reportDate + ",No Values (No Report)")
                continue

            # ---发布后最近的一次交易日---
            quote = bmQuotes.Get(stdDateTime, "Next")
            tradeDateTime = quote["DateTime"]

            # --- Period 1,2,3,4 X Year 1,2,3,4,5 ---
            # --- Period 4 means --> 仅基于年报 ---
            # --- Other means --> YoY Growth Rate, 同比增长 ---
            for year in range(1, 5+1):
                grwothRate, remark = GrowthBasedOnStatements(statements, statement, period=period, year=year, field=fieldName)
                if grwothRate != None:
                    #
                    document = GenarateDocument(symbol, tradeDateTime, grwothRate)
                    document["Period"] = period
                    document["Year"] = year
                    document["StdReportDate"] = stdReportDate
                    document["Remark"] = remark
                    #
                    growthDataSeries.Add(document)

        database.saveDataSeries(growthDataSeries)
        print("Calculated Growth Factor:" + symbol + " length:" + " count:" + str(count) + " @Time " + str(datetime.datetime.now()))


# --- YoY Growth Rate, 同比增长 ---
# --- 不做年化处理 ---
def GrowthBasedOnStatements(statements, statement, period, year, field):
    #
    symbol = statement["Symbol"]
    stdDateTime = statement["StdDateTime"]
    reportDate = statement["ReportDate"]
    stdReportDate = statement["StdReportDate"]
    currentYear = stdReportDate.year

    remark = None
    grwothRate = None
    # ---Find Previous Statement---
    stdReportDatePrevYear = Gadget.GenerateReportDate(currentYear - year, period)
    statementPrevious = Gadget.Find(statements, stdReportDatePrevYear, "Exact","StdReportDate")
    if statementPrevious == None:
        print(symbol + "," + reportDate + ",Field:" + field + ",Can't Find Prev Value Period:" + str(period) + " Year:" + str(year))

    # ---Check Error---
    elif field not in statement["Values"]:
        print(symbol + "," + statement["ReportDate"] + ",Field:" + field + ",Statement Missing Values")

    elif field not in statementPrevious["Values"]:
        print(symbol + "," + statementPrevious["ReportDate"] + ",Field:" + field + ",Previous Statement Missing Values")

    else:
        # ---Calculation---
        # ---不做 Compound Annual Growth Rate(CAGR) 处理---
        value = statement["Values"][field]
        previousValue = statementPrevious["Values"][field]
        grwothRate, remark = Algo.CalcGrowthRate(value, previousValue, period=1)

    return grwothRate, remark


# ---Obsolete---
def CalcGrowthFactor2(database, statementPrevious, statement, n, field = "OperatingProfit1", factorName = "Growth_CAGR_OperatingPrifit_Year1"):

    symbol = statement["Symbol"]

    #---Check Error---
    if field not in statement["Values"]:
        print(symbol + "," + statement["ReportDate"] + ",Factor:" + factorName + ",Missing Values Field " + field)
        return
    if field not in statementPrevious["Values"]:
        print(symbol + "," + statementPrevious["ReportDate"] + ",Factor:" + factorName + ",Missing Values Field " + field)
        return

    #---Calculation---
    grwothRate = Algo.CalcGrowhRate(statement["Values"][field], statementPrevious["Values"][field], n)

    #--- Insert to DataBase---
    if grwothRate != None:
        #factorParam = {}
        #factorParam["Period"] = n
        #factorParam["ReportDate"] = reportDate
        factorParam = "ReportDate:" + statement["ReportDate"] + ",Period:" + str(n)
        IO.WriteFactorToDataBase(database, symbol, factorName, statement["StdDateTime"], grwothRate, factorParam)


# ---Obsolete---
def CalcGrowthFactor3(database, statements, statement, n, field = "OperatingProfit1", factorName = "Growth_CAGR_OperatingPrifit_Year1", factorParam = "Year1"):

    symbol = statement["Symbol"]

    #---Build Datetime---
    datetime0 = statement["StdDateTime"]
    reportDate = statement["ReportDate"]
    reportDatetime = statement["ReportDateTime"]
    reportDatetimePrevYear = datetime.datetime(reportDatetime.year - n, 12, 31)
    reportDatetimePrevYear = Gadget.ToUTCDateTime(reportDatetimePrevYear)

    #---Find---
    statementPrevYear = Gadget.Find(statements,reportDatetimePrevYear,"Exact","ReportDateTime")
    if statementPrevYear == None:
        print(symbol + "," + reportDate + ",Factor:" + factorName  + ",Can't Find Prev Statement Dependend")
        return

    #
    if "Values" not in statement or len(statement["Values"]) < 93:
        print(symbol + "," + reportDate + ",Factor:" + factorName + ",Missing Values Field")
        return
    if "Values" not in statementPrevYear or len(statementPrevYear["Values"]) < 93:
        print(symbol + "," + reportDate + ",Factor:" + factorName + ",Missing Values Field")
        return

    #---Calculation---
    grwoth = CompoundAnnualGrowthRate(statement["Values"][field], statementPrevYear["Values"][field],n)

    #--- Insert to DataBase---
    if grwoth != None:
        WriteFactorToDataBase(database,symbol,factorName,datetime0,grwoth,factorParam)


# ---Obsolete---
def CalcGrowthFactor4(database, statements, statement, n, field="OperatingProfit1",
                      factorName="Growth_CAGR_OperatingPrifit_Year1", factorParam="Year1"):

    symbol = statement["Symbol"]

    # ---Build Datetime---
    datetime0 = statement["StdDateTime"]
    reportDate = statement["ReportDate"]
    reportDatetime = statement["ReportDateTime"]
    reportDatetime = Gadget.ToLocalDateTime(reportDatetime)
    reportDatetimePrevYear = datetime.datetime(reportDatetime.year - 1, reportDatetime.month, reportDatetime.day)
    reportDatetimePrevYear = Gadget.ToUTCDateTime(reportDatetimePrevYear)

    # ---Find---
    statementPrevYear = Gadget.Find(statements, reportDatetimePrevYear, "Exact", "ReportDateTime")
    if statementPrevYear == None:
        print(symbol + "," + reportDate + ",Factor:" + factorName + ",Can't Find Prev Statement Dependend")
        return

    #
    if "Values" not in statement or len(statement["Values"]) < 93:
        print(symbol + "," + reportDate + ",Factor:" + factorName + ",Missing Values Field")
        return
    if "Values" not in statementPrevYear or len(statementPrevYear["Values"]) < 93:
        print(symbol + "," + reportDate + ",Factor:" + factorName + ",Missing Values Field")
        return

    # ---Calculation---
    grwoth = CompoundAnnualGrowthRate(statement["Values"][field], statementPrevYear["Values"][field], n)

    # --- Insert to DataBase---
    if grwoth != None:
        WriteFactorToDataBase(database, symbol, factorName, datetime0, grwoth, factorParam)


if __name__ == '__main__':
    #
    from Core.Config import *
    config = Config()
    database = config.DataBase("MySQL")

    # ---DateTime---
    datetime1 = datetime.datetime(2019, 1, 1)
    datetime2 = datetime.datetime(2019, 9, 17)

    #
    stocks = database.Find("Instruments", "Stock")
    CalcGrowthFactor(database, stocks, datetime1, datetime2, update=False)

    #
    growthField = "OperatingCashFlow"
    year = 5
    period = 4
    params = {"Period": period, "Year": year, "GrowthType": "CAGR"}
    CalcFundamentalFactor(database, stocks, datetime1, datetime2,
                          factorName="Growth_CAGR_" + growthField + "_" + str(year) + "Yr",
                          callback=CAGRGrowthCallback,
                          fields=[growthField], params=params, outputField=["Remark"],
                          export=True,
                          update=True, test=False)


